#ifndef __MQ_exchange_
#define __MQ_EXCHANGE_

#include "../mqcommon/mq_log.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include "../mqcommon/mq_tools.hpp"
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <memory>
#include <mutex>

namespace MQ
{
    // 交换机类
    struct Exchange
    {
        using ptr = std::shared_ptr<Exchange>;

        std::string name;                                  // 交换机名称
        MQ::ExchangeType type;                             // 交换机类型
        bool durable;                                      // 持久化标志
        bool auto_delete;                                  // 是否自动删除
        google::protobuf::Map<std::string, std::string> args; // 其他参数

        Exchange(std::string ename, MQ::ExchangeType etype, bool durable, bool auto_delete, const google::protobuf::Map<std::string, std::string> &eargs)
            : name(ename), type(etype), durable(durable), auto_delete(auto_delete), args(eargs)
        {
        }
        Exchange()
        {
        }

        // 针对其他参数
        void setArgs(const std::string &str_args) // key=val&key=val&
        {
            std::vector<std::string> result;
            SplitHelper::split(str_args, "&", result);
            for (auto e : result)
            {
                size_t pos = e.find('=');
                std::string key = e.substr(0, pos);
                std::string val = e.substr(pos + 1);
                args[key] = val;
            }
        }

        std::string getArgs()
        {
            std::string result;
            for (auto e : args)
                result += e.first + "=" + e.second + "&";

            return result;
        }
    };
    // 交换机持久化管理类
    using ExchangeMap = std::unordered_map<std::string, Exchange::ptr>;
    class ExchangeMapper
    {
    public:
        ExchangeMapper(const std::string &dbfile)
            : _sql_helper(dbfile)
        {
            std::string path = FileHelper::parentDir(dbfile);
            FileHelper::createDirectory(path);
            assert(_sql_helper.open());
            createTable();
        }

        // insert
        bool insert_ex(Exchange::ptr &exp)
        {
            std::stringstream ss;
            ss << "insert into exchange_table values(";
            ss << "'" << exp->name << "'" << ", ";
            ss << exp->type << ", ";
            ss << exp->durable << ", ";
            ss << exp->auto_delete << ", ";
            ss << "'" << exp->getArgs() << "');";
            return _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        // delete exchange
        void delete_ex(const std::string &ename)
        {
            std::stringstream ss;
            ss << "delete from exchange_table where name=";
            ss << "'" << ename << "';";
            _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        // delete exchange table
        void delete_table()
        {
            std::stringstream ss;
            ss << "drop table if exists exchange_table;";
            bool ret = _sql_helper.exec(ss.str(), nullptr, nullptr);
            if (ret == false)
            {
                LOG(WARNING, "Exchange table delete fail!!!");
                abort();
            }
        }

        // recovery
        ExchangeMap recovery()
        {
            ExchangeMap result;
            std::string sql = "select name, type, durable, auto_delete, args from exchange_table";
            _sql_helper.exec(sql, selectCallback, &result);
            return result;
        }

    private:
        void createTable()
        {
            std::string create_table = "create table if not exists exchange_table(name varchar(32) primary key, type int, durable int, auto_delete int,args varchar(128));";
            bool ret = _sql_helper.exec(create_table, nullptr, nullptr);
            if (ret == false)
            {
                LOG(ERROR, "Createa table fail\n");
                abort(); // 异常退出
            }
        }

        static int selectCallback(void *args, int numcol, char **row, char **fields)
        {
            ExchangeMap *result = static_cast<ExchangeMap *>(args);
            auto exp = std::make_shared<Exchange>();
            exp->name = row[0];
            exp->type = static_cast<MQ::ExchangeType>(std::stoi(row[1]));
            exp->durable = static_cast<bool>(std::stoi(row[2]));
            exp->auto_delete = static_cast<bool>(std::stoi(row[3]));
            if (row[4])
                exp->setArgs(row[4]);

            result->insert(std::make_pair(exp->name, exp));
            return 0;
        }

    private:
        SqliteHelper _sql_helper;
    };

    // 交换机数据内存管理类
    class ExchangeManager
    {
    public:
        using ptr = std::shared_ptr<ExchangeManager>;

        ExchangeManager(const std::string &dbfile)
            : _mapper(dbfile)
        {
            _exchanges = _mapper.recovery();
        }

        // 声明交换机
        bool declareExchange(const std::string &name, MQ::ExchangeType type, bool durable, bool auto_delete, const google::protobuf::Map<std::string, std::string> &args)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if (it != _exchanges.end())
                return true;

            auto exp = std::make_shared<Exchange>(name, type, durable, auto_delete, args);
            // 判断是否需要持久化
            if (durable == true)
            {
                bool ret = _mapper.insert_ex(exp);
                if (ret == false)
                {
                    LOG(WARNING, "持久化插入失败!!!");
                    return false;
                }
            }
            _exchanges.insert(std::make_pair(name, exp));
            return true;
        }

        // 删除交换机
        void deleteExchange(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if (it == _exchanges.end())
                return;

            // if (it->second->durable == true) // 鲁棒性+1
            _mapper.delete_ex(name);

            _exchanges.erase(name);
        }

        // 获取指定交换机对象
        Exchange::ptr selectExchange(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if (it == _exchanges.end())
            {
                LOG(WARNING, "指定对象不存在!!!");
                return Exchange::ptr();
            }
            return it->second;
        }

        // 判断交换机是否存在
        bool exists(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if (it == _exchanges.end())
                return false;

            return true;
        }

        // 清理所有交换机数据
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.delete_table();
            _exchanges.clear();
        }

        // 测试size
        size_t size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _exchanges.size();
        }

    private:
        ExchangeMap _exchanges;
        ExchangeMapper _mapper;
        std::mutex _mutex;
    };
}
#endif